//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Reflection;

namespace OpenLS.Spreadsheet.Extensibility
{
    class ParameterConversionData
    {
        public ParameterConversionData(ParameterInfo parameter)
        {
            _converter = getConverterImp(parameter);
            Contract.Assert(Converter != null);
            IsParams = IsParameterParams(parameter);
            ParameterType = parameter.ParameterType;
            IsWorksheetContext = parameter.GetCustomAttributes(typeof(WorksheetContextAttribute), false).Length > 0;
            IsCellContext = parameter.GetCustomAttributes(typeof(CellContextAttribute), false).Length > 0;
            SetDefaultValueImp(parameter);
        }
        public object DefaultValue;
        public bool HasDefaultValue;
        private readonly IParameterConverter _converter;
        public  IParameterConverter Converter { get { return _converter; } }
        public readonly bool IsParams;
        public readonly Type ParameterType;
        public readonly bool IsWorksheetContext;
        public readonly bool IsCellContext;
        void SetDefaultValueImp(ParameterInfo p)
        {
            foreach (OptionalAttribute a in p.GetCustomAttributes(typeof(OptionalAttribute), false))
            {
                HasDefaultValue = true;
                DefaultValue = a.Value;
            }
        }

        static bool IsParameterParams(ParameterInfo p)
        {
            bool r = (p.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0);
            return r;
        }

        static void AddConverter (List<IParameterConverter> converters, IParameterConverter  converter)
        {
            Contract.Requires(converter != null);
            Contract.Requires(converters != null);
            Contract.Ensures(converters.Contains(converter));
            if (converters.Count == 0)
            {
                converters.Add(converter);
                return;
            }
            for (int i = converters.Count - 1; i >= 0; i--)
            {
                var previousConverter = converters[i];
                if (previousConverter.Priority >= converter.Priority)
                {
                    converters.Insert(i+1, converter);
                    return;
                }
            }
            converters.Insert(0, converter);
        }
        private static IParameterConverter getConverterImp(ParameterInfo parameterInfo)
        {
            Contract.Ensures(Contract.Result<IParameterConverter>() != null);
            List<IParameterConverter> converters = new List<IParameterConverter>();
            foreach (IParameterConverterProvider p in parameterInfo.GetCustomAttributes(typeof(IParameterConverterProvider), false))
                AddConverter(converters, p.GetConverter());
            foreach (IParameterConverterProvider p in (parameterInfo.Member.GetCustomAttributes(typeof(IParameterConverterProvider), false)))
                AddConverter(converters, p.GetConverter());
            foreach (IParameterConverterProvider p in (parameterInfo.Member.DeclaringType.GetCustomAttributes(typeof(IParameterConverterProvider), false)))
                AddConverter(converters, p.GetConverter());
            foreach (IParameterConverterProvider p in (parameterInfo.Member.DeclaringType.Assembly.GetCustomAttributes(typeof(IParameterConverterProvider), false)))
                AddConverter(converters, p.GetConverter());
            if (converters.Count == 0)
            {
                return new CompositeArgumentConverter(getDefaultConveters());
            }
            if (converters.Count == 1)
                return converters[0];
            return new CompositeArgumentConverter(converters);

        }
        static IEnumerable<IParameterConverter> getDefaultConveters()
        {
            yield return new AllowDateTimeToDoubleConversionAttribute();
            yield return new AllowDateTimeToIntegerConversionAttribute();
            yield return new AllowToStringConversionAttribute();
            yield return new CatchErrorsAttribute();
            yield return new AccessCellValueAttribute();
            yield return new NoStringToDoubleConversionAttribute();
            yield return new AllowDoubleToIntegerConversionAttribute();
            yield return new AllowToComplexConversionAttribute();

        }
    }
}
